/*
 * Copyrignt (c) xuzeshui.com. All Rights Reserved.
 * Author: Zeshui Xu<i@xuzeshui.com>
 * Created Time: 2015-09-29 21:12:16
 * Last Modified: 2017-07-22 13:14:23
 * File Name: login/thirdlogin.go
 * Description:
 */
package login

import (
	"time"

	"passport/account"
	"passport/errorcode"
	"passport/invitecode"
	"passport/models"
	"passport/user"

	"doubimeizhi.com/utility"

	log "github.com/cihub/seelog"
)

var (
	loginService *LoginService
)

/*
	第三方登录请求
*/
type ThirdAccountLoginReq struct {
	RegIp    string
	RegPlat  int8
	AKId     int
	ExtFrom  string `json:"ext_from"`
	ExtId    string `json:"ext_id"`
	NickName string `json:"nick_name"`
	Avatar   string `json:"avatar"`
	UnionId  string `json:"union_id"` //第三方开放平台不同应用同一用户给出的标识
	Mobile   string `json:"mobile"`   //绑定的手机号
	Captcha  string `json:"captcha"`  //绑定验证码
}



/*
	登录响应
*/
type LoginResp struct {
	AccessToken  string
	ExpireSecond int
	Userid       string
	HXAccount    string
	HXPasswd     string
}

/*
	第三方登录响应
*/
type ThirdAccountLoginResp struct {
	LoginResp
}

/*
	手机登录请求
*/
type MobileLoginReq struct {
	AKId     int
	Mobile   string `json:"mobile"`
	Password string `json:"password"`
}

/*
	手机登录响应
*/
type MobileLoginResp struct {
	LoginResp
}

/*
	第三方平台绑定关联
*/
type ThirdAccountBindLoginReq struct {
	RegIp     string `json:"client_ip"`
	RegPlat   int8
	AKId      int
	NeedLogin bool   //是否需要登录
	ExtFrom   string `json:"ext_from"`
	ExtId     string `json:"ext_id"`
	Mobile    string `json:"mobile"`
	NickName  string `json:"nick_name"`
	Avatar    string `json:"avatar"`
}

/*
	第三方平台绑定关联响应
*/
type ThirdAccountBindLoginResp struct {
	LoginResp
}

/*
	LoginService
*/
type LoginService struct{}

/*
	使用第三方账号登陆
	如果账号不存在,账号系统将自动创建一个用户(生成userid)
	随机生成一个唯一的access_token与userid绑定,并返回access_token和userid
*/
func (p *LoginService) ThirdAccountLogin(req *ThirdAccountLoginReq) (uint, string, *LoginResp) {
	if nil == req {
		log.Warnf("params for ThirdAccountLoginReq is NULL")
		return errorcode.EC_GL_INVALID_JSON_BODY, errorcode.Msg(errorcode.EC_GL_INVALID_JSON_BODY), nil
	}

	if !account.IsValidAccountType(req.ExtFrom) {
		log.Warnf("NOT support account type for %s", req.ExtFrom)
		return errorcode.EC_GL_INVALID_ACCOUNT_TYPE, errorcode.Msg(errorcode.EC_GL_INVALID_ACCOUNT_TYPE), nil
	}

	if req.ExtId == "" {
		log.Warnf("empty extid for %s, regip: %s, regplat: %d", req.ExtFrom, req.RegIp, req.RegPlat)
		return errorcode.EC_GL_INVALID_ACCOUNT_ID, errorcode.Msg(errorcode.EC_GL_INVALID_ACCOUNT_ID), nil
	}

	ai, err := account.GetAccountService().GetAllAccountInfo(req.ExtFrom, req.ExtId)
	if err != nil {
		log.Errorf("get userid for extFrom: %s, extId: %s error: %s", req.ExtFrom, req.ExtId, err.Error())
		return errorcode.EC_GL_INTERNAL_ERROR, errorcode.Msg(errorcode.EC_GL_INTERNAL_ERROR), nil
	}
	var userid string = ""
	if ai != nil {
		userid = ai.Userid
		//账号是否激活
		if ai.Active == account.AI_ACTIVE_NOLOGIN {
			log.Warnf("ThirdAccountLogin account is not active.  extFrom: %s, extId: %s", req.ExtFrom, req.ExtId)
			return errorcode.EC_LOGIN_ACCOUNT_FREEZE, "account is freezed", nil
		}
	} else {
		//针对IOS 1.2.0版本兼容处理尝试
		if 2 == req.RegPlat {
			/*
				FTLoginTypeDefault = 0,
				FTLoginTypeCooCaa ,
				FTLoginTypeWeChat,
				FTLoginTypeQQ,
				FTLoginTypeSinaWeibo
			*/
			var tryAccountType string
			if req.ExtFrom == account.ACCOUNT_TYPE_COOCAA {
				tryAccountType = account.ACCOUNT_TYPE_WECHAT
			} else if req.ExtFrom == account.ACCOUNT_TYPE_WECHAT {
				tryAccountType = account.ACCOUNT_TYPE_QQ
			} else if req.ExtFrom == account.ACCOUNT_TYPE_QQ {
				tryAccountType = account.ACCOUNT_TYPE_WEIBO
			}
			if tryAccountType != "" {
				ai, err = account.GetAccountService().GetAllAccountInfo(tryAccountType, req.ExtId)
				if err != nil {
					log.Errorf("get userid for extFrom: %s, extId: %s error: %s", tryAccountType, req.ExtId, err.Error())
					return errorcode.EC_GL_INTERNAL_ERROR, errorcode.Msg(errorcode.EC_GL_INTERNAL_ERROR), nil
				}
				if ai != nil {
					userid = ai.Userid
				}
			}
		}
	}
	//userid is not exists, so need create one
	if "" == userid {
		dispIC := invitecode.GetInviteCodeService().ObtainNewInviteCode() //分配一个邀请码

		//生成用户信息
		userid = utility.GenerateUUIDToken()
		uinfoChanger := user.NewUserInfoChanger(userid)
		uinfoChanger.SetUserid(userid)
		uinfoChanger.SetNickName(req.NickName)
		uinfoChanger.SetAvatar(req.Avatar)
		uinfoChanger.SetIsThirdAccount(account.AI_THIRD_ACCOUNT)
		uinfoChanger.SetRegIp(req.RegIp)
		uinfoChanger.SetRegPlat(req.RegPlat)
		uinfoChanger.SetInsertTime(time.Now().Format("2006-01-02 15:04:05"))
		uinfoChanger.SetInviteCode(dispIC)
		if err := uinfoChanger.Save(); err != nil {
			log.Errorf("uinfoChanger save error, msg: %s", err.Error())
			return errorcode.EC_GL_INTERNAL_ERROR, errorcode.Msg(errorcode.EC_GL_INTERNAL_ERROR), nil
		}

		//创建账号信息
		aichanger := account.NewAccountInfoChanger(req.ExtId, req.ExtFrom)
		aichanger.SetAccount(req.ExtId)
		aichanger.SetAccountType(req.ExtFrom)
		aichanger.SetUserid(userid)
		aichanger.SetActive(account.AI_ACTIVE_ALLOW_LOGIN)
		aichanger.SetNickName(req.NickName)
		aichanger.SetThirdAccount(account.AI_THIRD_ACCOUNT)
		aichanger.SetBindStatus(account.AI_BIND_STATUS)
		aichanger.SetInsertTime(time.Now().Format("2006-01-02 15:04:05"))

		if err := aichanger.Save(); err != nil {
			log.Errorf("aichanger save  error, msg: %s", err.Error())
			return errorcode.EC_GL_INTERNAL_ERROR, errorcode.Msg(errorcode.EC_GL_INTERNAL_ERROR), nil
		}

		//成功获取到邀请码
		if dispIC != "" {
			invitecode.GetInviteCodeService().CreateICAccount(userid, dispIC)
		}

		account.GetAccountBindService().RecordUserAccountBind(userid, req.ExtFrom, req.ExtId)
	} else {
		if req.NickName != "" && len(req.NickName) < 32 && req.NickName != ai.NickName {
			aichanger := account.NewAccountInfoChanger(ai.Account, ai.AccountType)
			aichanger.SetNickName(req.NickName)
			aichanger.Save()
			log.Infof("change account nickname from %s to %s. extId: %s, extId: %s", ai.NickName, req.NickName, req.ExtFrom, req.ExtId)
		}
	}

	accessToken, ok := account.GetAccessTokenService().GenerateAndRecordAccessToken(userid, req.AKId)
	if !ok {
		return errorcode.EC_GL_INTERNAL_ERROR, errorcode.Msg(errorcode.EC_GL_INTERNAL_ERROR), nil
	}
	log.Infof("login extFrom: %s, extId: %s with accessToekn: %s, userid: %s", req.ExtFrom, req.ExtId, accessToken, userid)
	resp := &LoginResp{}
	resp.AccessToken = accessToken
	resp.Userid = userid
	resp.ExpireSecond = models.GetAccessTokenExpireSecond()

	return errorcode.EC_GL_SUCCESS, errorcode.Msg(errorcode.EC_GL_SUCCESS), resp
}

/*
	手机登录处理
*/
func (p *LoginService) MobilePassLogin(req *MobileLoginReq) (uint, string, *MobileLoginResp) {
	if nil == req {
		log.Warnf("params for MobilePassLogin is NULL")
		return errorcode.EC_GL_INVALID_JSON_BODY, errorcode.Msg(errorcode.EC_GL_INVALID_JSON_BODY), nil
	}

	if !utility.IsValidMainlandMobile(req.Mobile) {
		log.Warnf("MobilePassLogin invalid mobile: [%s]", req.Mobile)
		return errorcode.EC_CAPTCHA_INVALID_MOBILE_NUMBER, "invalid mobile number", nil
	}

	if req.Password == "" {
		return errorcode.EC_LOGIN_WITHOUT_PASSWD, "missing password", nil
	}

	ai, aiErr := account.GetAccountService().GetAllAccountInfo(account.ACCOUNT_TYPE_MOBILE, req.Mobile)
	if aiErr != nil {
		return errorcode.EC_GL_INTERNAL_ERROR, errorcode.Msg(errorcode.EC_GL_INTERNAL_ERROR), nil
	} else if ai == nil {
		return errorcode.EC_LOGIN_ACCOUNT_NOT_EXISTS, "account is not exists", nil
	}

	//账号是否已解绑
	if ai.BindStatus == account.AI_UNBIND_STATUS {
		log.Warnf("MobilePassLogin account is not bind. mobile: %s", req.Mobile)
		return errorcode.EC_LOGIN_ACCOUNT_NOT_EXISTS, "account is not exists", nil
	}

	//账号是否激活
	if ai.Active == account.AI_ACTIVE_NOLOGIN {
		log.Warnf("MobilePassLogin account is not active. mobile: %s", req.Mobile)
		return errorcode.EC_LOGIN_ACCOUNT_FREEZE, "account is freezed", nil
	}

	ui, uiErr := user.GetUserService().GetAllUserInfo(ai.Userid)
	if uiErr != nil {
		log.Errorf("GetAllUserInfo error for userid: [%s], msg: %s", ai.Userid, uiErr.Error())
		return errorcode.EC_GL_INTERNAL_ERROR, errorcode.Msg(errorcode.EC_GL_INTERNAL_ERROR), nil
	} else if ui == nil {
		log.Errorf("GetAllUserInfo error for userid: [%s], msg: not exists userinfo", ai.Userid)
		return errorcode.EC_GL_INTERNAL_ERROR, errorcode.Msg(errorcode.EC_GL_INTERNAL_ERROR), nil
	}

	//检验密码是否正确
	if ui.Password != req.Password {
		return errorcode.EC_LOGIN_INCORRECT_PASSWORD, "incorrect password", nil
	}

	userid := ui.UserId
	accessToken, ok := account.GetAccessTokenService().GenerateAndRecordAccessToken(userid, req.AKId)
	if !ok {
		log.Errorf("GenerateAndRecordAccessToken error for userid: %s", userid)
		return errorcode.EC_GL_INTERNAL_ERROR, errorcode.Msg(errorcode.EC_GL_INTERNAL_ERROR), nil
	}
	log.Infof("login mobile success, mobile: %s with accessToekn: %s, userid: %s", req.Mobile, accessToken, userid)
	resp := &MobileLoginResp{}
	resp.AccessToken = accessToken
	resp.Userid = userid
	resp.ExpireSecond = models.GetAccessTokenExpireSecond()

	return errorcode.EC_GL_SUCCESS, errorcode.Msg(errorcode.EC_GL_SUCCESS), resp
}

/*
	自动登录
*/
func (p *LoginService) AutoLogin(userid string, akid int) (uint, string, *MobileLoginResp) {
	accessToken, ok := account.GetAccessTokenService().GenerateAndRecordAccessToken(userid, akid)
	if !ok {
		log.Errorf("GenerateAndRecordAccessToken error for userid: %s", userid)
		return errorcode.EC_GL_INTERNAL_ERROR, errorcode.Msg(errorcode.EC_GL_INTERNAL_ERROR), nil
	}

	log.Infof("autologin success, userid: %s", userid)
	resp := &MobileLoginResp{}
	resp.AccessToken = accessToken
	resp.Userid = userid
	resp.ExpireSecond = models.GetAccessTokenExpireSecond()
	return errorcode.EC_GL_SUCCESS, errorcode.Msg(errorcode.EC_GL_SUCCESS), resp
}

/*
	第三方平台账号关联绑定登录
*/
func (p *LoginService) ThirdAccountBindLogin(req *ThirdAccountBindLoginReq) (uint, string, *ThirdAccountBindLoginResp) {
	if nil == req {
		log.Warnf("params for ThirdAccountBindLogin is NULL")
		return errorcode.EC_GL_INVALID_JSON_BODY, errorcode.Msg(errorcode.EC_GL_INVALID_JSON_BODY), nil
	}

	if !account.IsValidAccountType(req.ExtFrom) {
		log.Warnf("NOT support account type for %s", req.ExtFrom)
		return errorcode.EC_GL_INVALID_ACCOUNT_TYPE, errorcode.Msg(errorcode.EC_GL_INVALID_ACCOUNT_TYPE), nil
	}

	if req.ExtId == "" {
		log.Warnf("empty extid for %s, regip: %s, regplat: %d", req.ExtFrom, req.RegIp, req.RegPlat)
		return errorcode.EC_GL_INVALID_ACCOUNT_ID, errorcode.Msg(errorcode.EC_GL_INVALID_ACCOUNT_ID), nil
	}

	//openid情况
	ai, err := account.GetAccountService().GetAllAccountInfo(req.ExtFrom, req.ExtId)
	if err != nil {
		log.Errorf("get userid for extFrom: %s, extId: %s error: %s", req.ExtFrom, req.ExtId, err.Error())
		return errorcode.EC_GL_INTERNAL_ERROR, errorcode.Msg(errorcode.EC_GL_INTERNAL_ERROR), nil
	}
	var userid string = ""
	var openidUserid string = ""
	var mobileUserid string = ""
	var isAccountActive bool = false
	if ai != nil {
		userid = ai.Userid
		openidUserid = ai.Userid
		isAccountActive = ai.Active == account.AI_ACTIVE_ALLOW_LOGIN
	}

	//手机号情况
	if ai, err := account.GetAccountService().GetAllAccountInfo(account.ACCOUNT_TYPE_MOBILE, req.Mobile); err == nil {
		if ai != nil {
			mobileUserid = ai.Userid
			if openidUserid == "" { //当openid没绑定
				userid = ai.Userid
				isAccountActive = ai.Active == account.AI_ACTIVE_ALLOW_LOGIN
			}
		}
	} else { //查询手机账号出错
		log.Errorf("get userid for mobile: %s error: %s", req.Mobile, err.Error())
		return errorcode.EC_GL_INTERNAL_ERROR, errorcode.Msg(errorcode.EC_GL_INTERNAL_ERROR), nil
	}

	if userid == "" { //均未关联绑定过,自动创建账号,并关联
		dispIC := invitecode.GetInviteCodeService().ObtainNewInviteCode() //分配一个邀请码

		//生成用户信息
		userid = utility.GenerateUUIDToken()
		uinfoChanger := user.NewUserInfoChanger(userid)
		uinfoChanger.SetUserid(userid)
		uinfoChanger.SetNickName(req.NickName)
		uinfoChanger.SetAvatar(req.Avatar)
		uinfoChanger.SetMobile(req.Mobile)
		uinfoChanger.SetIsThirdAccount(account.AI_SELF_ACCOUNT)
		uinfoChanger.SetRegIp(req.RegIp)
		uinfoChanger.SetRegPlat(req.RegPlat)
		uinfoChanger.SetInsertTime(time.Now().Format("2006-01-02 15:04:05"))
		uinfoChanger.SetInviteCode(dispIC)
		if err := uinfoChanger.Save(); err != nil {
			log.Errorf("uinfoChanger save error, msg: %s", err.Error())
			return errorcode.EC_GL_INTERNAL_ERROR, errorcode.Msg(errorcode.EC_GL_INTERNAL_ERROR), nil
		}

		accountBindReq := &account.AccountBindInfoReq{}

		//创建手机账号
		accountBindReq.ExtFrom = account.ACCOUNT_TYPE_MOBILE
		accountBindReq.ExtId = req.Mobile
		accountBindReq.NickName = req.NickName
		if code, msg := account.GetAccountBindService().AccountBind(userid, false, accountBindReq); code != errorcode.EC_GL_SUCCESS {
			return code, msg, nil
		}

		//创建opeid账号
		accountBindReq.ExtFrom = req.ExtFrom
		accountBindReq.ExtId = req.ExtId
		if code, msg := account.GetAccountBindService().AccountBind(userid, true, accountBindReq); code != errorcode.EC_GL_SUCCESS {
			return code, msg, nil
		}

		//成功获取到邀请码
		if dispIC != "" {
			invitecode.GetInviteCodeService().CreateICAccount(userid, dispIC)
		}
	} else if openidUserid == "" { //手机号存在,但openid是新的
		accountBindReq := &account.AccountBindInfoReq{}
		accountBindReq.ExtFrom = req.ExtFrom
		accountBindReq.ExtId = req.ExtId
		accountBindReq.NickName = req.NickName
		if code, msg := account.GetAccountBindService().AccountBind(userid, true, accountBindReq); code != errorcode.EC_GL_SUCCESS && code != errorcode.EC_ACCOUNT_BIND_BINDED_BY_OTHER {
			return code, msg, nil
		}
	} else if mobileUserid == "" { //openid存在,但手机号未绑定
		accountBindReq := &account.AccountBindInfoReq{}
		accountBindReq.ExtFrom = account.ACCOUNT_TYPE_MOBILE
		accountBindReq.ExtId = req.Mobile
		accountBindReq.NickName = req.NickName
		if code, msg := account.GetAccountBindService().AccountBind(userid, false, accountBindReq); code != errorcode.EC_GL_SUCCESS && code != errorcode.EC_ACCOUNT_BIND_BINDED_BY_OTHER {
			return code, msg, nil
		} else if code == errorcode.EC_GL_SUCCESS { //手机号绑定成功
			uc := user.NewUserInfoChanger(userid)
			uc.SetMobile(req.Mobile)
			uc.SetPassword(req.Mobile[5:]) //初始密码手机号后6位
			uc.SetIsThirdAccount(account.AI_SELF_ACCOUNT)
			uc.Save()
		}
	}

	if openidUserid != "" { //openid存在的情况,修改昵称
		if req.NickName != "" && len(req.NickName) < 32 && req.NickName != ai.NickName {
			aichanger := account.NewAccountInfoChanger(ai.Account, ai.AccountType)
			aichanger.SetNickName(req.NickName)
			aichanger.Save()
			log.Infof("change account nickname from %s to %s. extId: %s, extId: %s", ai.NickName, req.NickName, req.ExtFrom, req.ExtId)
		}

	}

	log.Infof("bindlogin extFrom: %s, extId: %s, mobile: %s, userid: %s", req.ExtFrom, req.ExtId, req.Mobile, userid)
	resp := &ThirdAccountBindLoginResp{}
	resp.Userid = userid
	if req.NeedLogin {
		if !isAccountActive {
			log.Warnf("bindlogin account is not active.  extFrom: %s, extId: %si, mobile: %s, userid: %s", req.ExtFrom, req.ExtId, req.Mobile, userid)
			return errorcode.EC_LOGIN_ACCOUNT_FREEZE, "account is freezed", nil
		}
		accessToken, ok := account.GetAccessTokenService().GenerateAndRecordAccessToken(userid, req.AKId)
		if !ok {
			log.Errorf("GenerateAndRecordAccessToken error, msg: %s", err.Error())
			return errorcode.EC_GL_INTERNAL_ERROR, errorcode.Msg(errorcode.EC_GL_INTERNAL_ERROR), nil
		}
		resp.AccessToken = accessToken
		resp.ExpireSecond = models.GetAccessTokenExpireSecond()
	}

	return errorcode.EC_GL_SUCCESS, errorcode.Msg(errorcode.EC_GL_SUCCESS), resp
}

/*
	LoginService处理对象
*/
func GetLoginService() *LoginService {
	return loginService
}

func init() {
	loginService = &LoginService{}
}
